package net.realsee.service.wcs.intf.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.RequiredArgsConstructor;
import net.realsee.common.config.AppConfig;
import net.realsee.common.enums.wcs.intf.InterfaceMethodEnum;
import net.realsee.common.enums.wcs.intf.InterfaceResultCodeEnum;
import net.realsee.common.exception.ServiceException;
import net.realsee.common.utils.ServletUtils;
import net.realsee.common.utils.StringUtils;
import net.realsee.domain.wcs.intf.WcsExtApp;
import net.realsee.domain.wcs.intf.WcsIntf;
import net.realsee.domain.wcs.intf.WcsIntfInlog;
import net.realsee.domain.wcs.intf.bo.InterfaceResponse;
import net.realsee.domain.wcs.intf.bo.WcsIntfInlogBo;
import net.realsee.domain.wcs.intf.vo.WcsIntfDataVo;
import net.realsee.domain.wcs.intf.vo.WcsIntfInlogVo;
import net.realsee.framework.core.domain.PageQuery;
import net.realsee.framework.core.page.TableDataInfo;
import net.realsee.mapper.wcs.intf.WcsIntfInlogMapper;
import net.realsee.service.wcs.intf.IWcsExtAppService;
import net.realsee.service.wcs.intf.IWcsIntfDataService;
import net.realsee.service.wcs.intf.IWcsIntfInlogService;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import javax.servlet.http.HttpServletRequest;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.Map;

/**
 * Inbound接口日志Service业务层处理
 *
 * @author realsee
 * @date 2023-08-11
 */
@RequiredArgsConstructor
@Service
public class WcsIntfInlogServiceImpl extends ServiceImpl<WcsIntfInlogMapper, WcsIntfInlog> implements IWcsIntfInlogService {

    private final WcsIntfInlogMapper baseMapper;
    private final IWcsIntfDataService dataService;
    private final IWcsExtAppService extAppService;
    private final AppConfig appConfig;

    /**
     * 日志记录ID变量
     * 作用：方法结束后用logId定位日志去更新
     */
    private static final ThreadLocal<Long> logIdThreadLocal = new ThreadLocal<>();

    /**
     * 查询Inbound接口日志
     */
    @Override
    public WcsIntfInlogVo selectVoById(Long id) {
        WcsIntfInlogVo result = baseMapper.selectVoById(id);
        if (ObjectUtil.isNotNull(result) && ObjectUtil.isNotNull(result.getReceiveDataId()) && 0L != result.getReceiveDataId()) {
            WcsIntfDataVo outputDataVo = dataService.selectVoById(result.getReceiveDataId());
            if (ObjectUtil.isNotNull(outputDataVo) && StrUtil.isNotBlank(outputDataVo.getContent())) {
                result.setReceiveData(StringUtils.beautify(outputDataVo.getContent()));
            }
        }
        if (ObjectUtil.isNotNull(result) && ObjectUtil.isNotNull(result.getFeedbackDataId()) && 0L != result.getFeedbackDataId()) {
            WcsIntfDataVo feedbackDataVo = dataService.selectVoById(result.getFeedbackDataId());
            if (ObjectUtil.isNotNull(feedbackDataVo) && StrUtil.isNotBlank(feedbackDataVo.getContent())) {
                result.setFeedbackData(StringUtils.beautify(feedbackDataVo.getContent()));
            }
        }
        return result;
    }

    /**
     * 查询Inbound接口日志列表
     */
    @Override
    public TableDataInfo<WcsIntfInlogVo> selectVoPage(WcsIntfInlogBo bo, PageQuery pageQuery) {
        LambdaQueryWrapper<WcsIntfInlog> lqw = buildVoQueryWrapper(bo);
        Page<WcsIntfInlogVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
        return TableDataInfo.build(result);
    }

    /**
     * 查询Inbound接口日志列表
     */
    @Override
    public List<WcsIntfInlogVo> selectVoList(WcsIntfInlogBo bo) {
        LambdaQueryWrapper<WcsIntfInlog> lqw = buildVoQueryWrapper(bo);
        return baseMapper.selectVoList(lqw);
    }

    private LambdaQueryWrapper<WcsIntfInlog> buildVoQueryWrapper(WcsIntfInlogBo bo) {
        Map<String, Object> params = bo.getParams();
        LambdaQueryWrapper<WcsIntfInlog> lqw = Wrappers.lambdaQuery();
        lqw.eq(bo.getId() != null, WcsIntfInlog::getId, bo.getId());
        lqw.eq(StringUtils.isNotBlank(bo.getTaskId()), WcsIntfInlog::getTaskId, bo.getTaskId());
        lqw.eq(StringUtils.isNotBlank(bo.getAppCode()), WcsIntfInlog::getAppCode, bo.getAppCode());
        lqw.eq(StringUtils.isNotBlank(bo.getTaskAppCode()), WcsIntfInlog::getTaskAppCode, bo.getTaskAppCode());
        lqw.eq(StringUtils.isNotBlank(bo.getInterfaceCode()), WcsIntfInlog::getInterfaceCode, bo.getInterfaceCode());
        lqw.like(StringUtils.isNotBlank(bo.getInterfaceName()), WcsIntfInlog::getInterfaceName, bo.getInterfaceName());
        lqw.eq(StringUtils.isNotBlank(bo.getInterfaceType()), WcsIntfInlog::getInterfaceType, bo.getInterfaceType());
        lqw.eq(StringUtils.isNotBlank(bo.getOperationCode()), WcsIntfInlog::getOperationCode, bo.getOperationCode());
        lqw.eq(bo.getReceivedOn() != null, WcsIntfInlog::getReceivedOn, bo.getReceivedOn());
        lqw.eq(bo.getFeedbackOn() != null, WcsIntfInlog::getFeedbackOn, bo.getFeedbackOn());
        lqw.eq(StringUtils.isNotBlank(bo.getUrl()), WcsIntfInlog::getUrl, bo.getUrl());
        lqw.eq(StringUtils.isNotBlank(bo.getCallbackUrl()), WcsIntfInlog::getCallbackUrl, bo.getCallbackUrl());
        lqw.eq(StringUtils.isNotBlank(bo.getMethod()), WcsIntfInlog::getMethod, bo.getMethod());
        lqw.eq(StringUtils.isNotBlank(bo.getHeader()), WcsIntfInlog::getHeader, bo.getHeader());
        lqw.eq(StringUtils.isNotBlank(bo.getAuthType()), WcsIntfInlog::getAuthType, bo.getAuthType());
        lqw.eq(StringUtils.isNotBlank(bo.getAuthData()), WcsIntfInlog::getAuthData, bo.getAuthData());
        lqw.eq(bo.getHttpCode() != null, WcsIntfInlog::getHttpCode, bo.getHttpCode());
        lqw.eq(StringUtils.isNotBlank(bo.getResultCode()), WcsIntfInlog::getResultCode, bo.getResultCode());
        lqw.eq(StringUtils.isNotBlank(bo.getMessage()), WcsIntfInlog::getMessage, bo.getMessage());
        lqw.eq(bo.getStatus() != null, WcsIntfInlog::getStatus, bo.getStatus());
        lqw.eq(bo.getMaxRetry() != null, WcsIntfInlog::getMaxRetry, bo.getMaxRetry());
        lqw.eq(bo.getRetryCounter() != null, WcsIntfInlog::getRetryCounter, bo.getRetryCounter());
        lqw.eq(StringUtils.isNotBlank(bo.getKeyword1()), WcsIntfInlog::getKeyword1, bo.getKeyword1());
        lqw.eq(StringUtils.isNotBlank(bo.getKeyword2()), WcsIntfInlog::getKeyword2, bo.getKeyword2());
        lqw.eq(StringUtils.isNotBlank(bo.getParTaskId()), WcsIntfInlog::getParTaskId, bo.getParTaskId());
        lqw.eq(StringUtils.isNotBlank(bo.getReceiveData()), WcsIntfInlog::getReceiveData, bo.getReceiveData());
        lqw.eq(StringUtils.isNotBlank(bo.getFeedbackData()), WcsIntfInlog::getFeedbackData, bo.getFeedbackData());
        lqw.eq(bo.getReceiveDataId() != null, WcsIntfInlog::getReceiveDataId, bo.getReceiveDataId());
        lqw.eq(bo.getFeedbackDataId() != null, WcsIntfInlog::getFeedbackDataId, bo.getFeedbackDataId());
        lqw.eq(bo.getOrderHeaderId() != null, WcsIntfInlog::getOrderHeaderId, bo.getOrderHeaderId());
        lqw.eq(bo.getOrderDetailId() != null, WcsIntfInlog::getOrderDetailId, bo.getOrderDetailId());
        lqw.eq(StringUtils.isNotBlank(bo.getFuid()), WcsIntfInlog::getFuid, bo.getFuid());
        lqw.eq(bo.getTextId() != null, WcsIntfInlog::getTextId, bo.getTextId());
        lqw.eq(bo.getRefrenceId() != null, WcsIntfInlog::getRefrenceId, bo.getRefrenceId());
        lqw.eq(bo.getUnitId() != null, WcsIntfInlog::getUnitId, bo.getUnitId());
        lqw.eq(bo.getDelFlag() != null, WcsIntfInlog::getDelFlag, bo.getDelFlag());
        lqw.eq(StringUtils.isNotBlank(bo.getCreateBy()), WcsIntfInlog::getCreateBy, bo.getCreateBy());
        lqw.eq(bo.getCreateTime() != null, WcsIntfInlog::getCreateTime, bo.getCreateTime());
        lqw.eq(StringUtils.isNotBlank(bo.getUpdateBy()), WcsIntfInlog::getUpdateBy, bo.getUpdateBy());
        lqw.eq(bo.getUpdateTime() != null, WcsIntfInlog::getUpdateTime, bo.getUpdateTime());
        lqw.eq(StringUtils.isNotBlank(bo.getDeleteBy()), WcsIntfInlog::getDeleteBy, bo.getDeleteBy());
        lqw.eq(bo.getDeleteTime() != null, WcsIntfInlog::getDeleteTime, bo.getDeleteTime());
        lqw.eq(bo.getRowVersionStamp() != null, WcsIntfInlog::getRowVersionStamp, bo.getRowVersionStamp());
        lqw.eq(StringUtils.isNotBlank(bo.getRemark()), WcsIntfInlog::getRemark, bo.getRemark());
        lqw.orderByDesc(WcsIntfInlog::getId);
        return lqw;
    }

    /**
     * 新增Inbound接口日志
     */
    @Override
    public Boolean insertByBo(WcsIntfInlogBo bo) {
        WcsIntfInlog add = BeanUtil.toBean(bo, WcsIntfInlog.class);
        return this.save(add);
    }

    /**
     * 修改Inbound接口日志
     */
    @Override
    public Boolean updateByBo(WcsIntfInlogBo bo) {
        WcsIntfInlog update = BeanUtil.toBean(bo, WcsIntfInlog.class);
        return this.updateById(update);
    }

    /**
     * 批量删除Inbound接口日志
     */
    @Override
    public Boolean deleteWithValidByIds(Collection<Long> ids) {
        return this.removeBatchByIds(ids);
    }

    /**
     * 新增Inbound接口日志
     */
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    @Override
    public WcsIntfInlog insertByRequest(HttpServletRequest request, WcsIntf intf) {
        if (ObjectUtil.isNull(request) || ObjectUtil.isNull(intf) || !intf.isRecord()) {
            // 不需要记录日志
            return null;
        }

        WcsExtApp extApp = extAppService.getByAppCode(intf.getAppCode());
        if (ObjectUtil.isNull(extApp)) {
            throw new ServiceException(StrUtil.format("系统定义对象错误, appCode={}", intf.getAppCode()));
        }

        // 请求参数
        String apiUrl = request.getRequestURI();
        Map<String, String> paramsMap = ServletUtils.getParamMap(request);
        if (CollUtil.isNotEmpty(paramsMap)) {
            apiUrl = apiUrl + "?" + HttpUtil.toParams(paramsMap);
        }
        // 请求体
        String jsonBody = StrUtil.EMPTY_JSON;
        if (isJsonRequest(request)) {
            try {
                jsonBody = IoUtil.read(request.getReader());
            } catch (Exception ignored) {
            }
        }

        WcsIntfInlog inlog = new WcsIntfInlog();
        inlog.setTaskId(IdUtil.getSnowflakeNextId() + "");
        inlog.setAppCode(appConfig.getName());
        inlog.setTaskAppCode(intf.getAppCode());
        inlog.setInterfaceCode(intf.getInterfaceCode());
        inlog.setInterfaceName(intf.getInterfaceName());
        inlog.setInterfaceType(intf.getInterfaceType());
        //inlog.setOperationCode();
        inlog.setReceivedOn(new Date());
        inlog.setUrl(apiUrl);
        //inlog.setCallbackUrl();
        inlog.setMethod(intf.getMethod());
        //inlog.setHeader();
        inlog.setAuthType(intf.getAuthType());
        inlog.setAuthData(intf.getAuthData());
        //inlog.setHttpCode();
        //inlog.setResultCode();
        //inlog.setMessage();
        //inlog.setStatus();
        inlog.setMaxRetry(intf.getMaxRetry());
        inlog.setRetryCounter(1L);
        // 入参
        inlog.setReceiveDataId(dataService.insertByContent(jsonBody));
        //inlog.setFeedbackDataId();
        inlog.setFuid(IdUtil.fastUUID());
        baseMapper.insert(inlog);

        logIdThreadLocal.set(inlog.getId());
        return inlog;
    }

    /**
     * 修改Inbound接口日志
     */
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    @Override
    public WcsIntfInlog updateByResponse(InterfaceResponse response) {
        Long logId = logIdThreadLocal.get();
        if (ObjectUtil.isNotNull(response.getLogId()) && response.getLogId() != 0L) {
            logId = response.getLogId();
        }
        WcsIntfInlog inlog = this.getById(logId);
        if (ObjectUtil.isNotNull(inlog)) {
            inlog.setFeedbackOn(new Date());
            if (ObjectUtil.isNotNull(response)) {
                inlog.setKeyword1(response.getKeyword1());
                inlog.setKeyword2(response.getKeyword2());
                inlog.setHttpCode(response.getHttpCode());
                inlog.setStatus(response.getStatus());
                inlog.setFeedbackDataId(dataService.insertByContent(response.getContent()));
                if (StrUtil.isNotBlank(response.getTaskId())) {
                    inlog.setTaskId(response.getTaskId());
                }
            }
            this.updateById(inlog);
        }
        return inlog;
    }

    /**
     * 删除线程中 日志记录ID变量
     * 注意：ThreadLocal存在内存泄漏问题，使用后需要及时删除
     */
    @Override
    public void removeLogIdThreadLocal() {
        // 判断一下业务代码有没有去更新入站日志，如果没更新，在此处进行更新一下
        WcsIntfInlog inlog = this.getById(logIdThreadLocal.get());
        if (ObjectUtil.isNotNull(inlog) && ObjectUtil.isNull(inlog.getFeedbackOn())) {
            inlog.setFeedbackOn(new Date());
            inlog.setHttpCode(200L);
            inlog.setStatus(InterfaceResultCodeEnum.SUCCESS.getCode());
            this.updateById(inlog);
        }
        logIdThreadLocal.remove();
    }

    /**
     * 接口重试
     */
    @Override
    public Boolean retry(WcsIntfInlogBo bo) {
        if (ObjectUtil.isNull(bo) || ObjectUtil.isNull(bo.getId())) {
            return false;
        }
        WcsIntfInlog intfInlog = this.getById(bo.getId());
        // 获取HOST
        String hostAddr = ServletUtils.getRequest().getLocalAddr();
        hostAddr = HttpUtil.isHttp(hostAddr) ? hostAddr : StrUtil.format("http://{}", hostAddr);
        // 获取端口
        int httpInterfacePort = ServletUtils.getRequest().getLocalPort();
        // 创建Http请求对象
        HttpRequest httpRequest = HttpUtil.createRequest(InterfaceMethodEnum.getMethod(intfInlog.getMethod()),
            StrUtil.format("{}:{}{}", hostAddr, httpInterfacePort, intfInlog.getUrl()));
        // 获取Body内容
        if (ObjectUtil.isNotNull(intfInlog.getReceiveDataId()) && 0L != intfInlog.getReceiveDataId()) {
            WcsIntfDataVo outputDataVo = dataService.selectVoById(intfInlog.getReceiveDataId());
            if (ObjectUtil.isNotNull(outputDataVo) && StrUtil.isNotBlank(outputDataVo.getContent())) {
                httpRequest.body(outputDataVo.getContent());
            }
        }
        httpRequest.execute(true);
        return true;
    }

    /**
     * 判断本次请求的数据类型是否为json
     *
     * @param request request
     * @return boolean
     */
    private boolean isJsonRequest(HttpServletRequest request) {
        String contentType = request.getContentType();
        if (contentType != null) {
            return StringUtils.startsWithIgnoreCase(contentType, MediaType.APPLICATION_JSON_VALUE);
        }
        return false;
    }
}
